﻿using Cofe.Core.Property;
using Cofe.Core.Utils;
using Cofe.Core.Validater;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Linq.Expressions;

namespace Cofe.Core.Implements
{
    [Export(typeof(ICofeService))]
    [ServicePriority(ServicePriorityAttribute.DefaultPriority_COFE)]
    public class PropertyDictionary : IPropertyDictionary
    {
        #region Constructor

        public PropertyDictionary()
        {
            PropertyInvoker = new PropertyInvoker(this);
            AttributeHelper = new PropertyAttributeHelper() { PropertyDictionary = this };
        }

        public PropertyDictionary(params IPropertyDefinitions[] propertyDefinitions)
            : this()
        {
            PropertyDefinitions = propertyDefinitions;
        }

        #endregion

        #region Methods

        private void refresh()
        {
            var properties = _definitions.SelectMany((d) => d.GetSupportedProperties());
            _propertyDic = properties.ToDictionary((p) => EnumUtils.GetFullName(p), (p) => p);
            _propertyIdDic = properties.ToDictionary((p) => (int)p, (p) => p);
            _propertyDefValueDic = properties.Where((p) => AttributeHelper.HasDefaultValue(p))
                .ToDictionary((p) => p, p => AttributeHelper.GetDefaultValue(p));
        }

        public List<object> GetPropertiesWithDefaultValue()
        {
            return (from value in _propertyDefValueDic.Keys
                    where AttributeHelper.HasDefaultValue(value)
                    select value).ToList();
        }

        public object GetProperty(string propertyString)
        {
            if (_propertyDic.ContainsKey(propertyString))
                return _propertyDic[propertyString];

            foreach (var ppair in _propertyDic)
            {
                string resName = EnumUtils.GetResourceName(ppair.Value, false);
                string alias = DecoratorExtension.getAlias(ppair.Value);
                if (resName != null &&
                    (
                    propertyString.Equals(resName, StringComparison.OrdinalIgnoreCase) ||
                    propertyString.Equals(alias, StringComparison.OrdinalIgnoreCase)
                    ))
                    return ppair.Value;
            }

            return null;
        }

        public object GetProperty(int propertyId)
        {
            if (_propertyIdDic.ContainsKey(propertyId))
                return _propertyIdDic[propertyId];
            return null;
        }


        public string GetPropertyString(object property)
        {
            return EnumUtils.GetResourceName(property);
        }



        public IPropertyDefinitions GetPropertyDefinition(object property)
        {
            int propId = (int)property;
            return this.PropertyDefinitions.FindDefinition(propId);
        }


        public Expression<Func<CofeSystemInfo, T>> GetPropertyExpression<T>(object property)
        {
            var pd = GetPropertyDefinition(property);
            return pd == null ? null : pd.Expressions.GetPropertyExpression<T>(property);
        }

        public bool HasProperty(object property)
        {
            foreach (var ppair in _propertyDic)
                if (ppair.Value.Equals(property))
                    return true;
            return false;
        }

        public bool HasProperty(string propertyString)
        {
            return GetProperty(propertyString) != null;
        }



        public IPropertyDefinitions[] CreatePropertyDefinitions(IEnumerable<object> properties,
            params IPropertyDefinitions[] propertyDefinitionsSet)
        {
            if (propertyDefinitionsSet == null || propertyDefinitionsSet.Length == 0)
            {
                propertyDefinitionsSet = this.PropertyDefinitions;

                return (from sd in propertyDefinitionsSet
                        select sd.Subset(properties.ToArray()))
                        .Where(pd => pd.GetSupportedProperties().Any())
                        .ToArray();
            }
            else
                return (from sd in propertyDefinitionsSet
                        select sd.Subset(properties.ToArray()))
                        .ToArray();
        }

        #endregion

        #region Data

        Dictionary<string, object> _propertyDic = new Dictionary<string, object>();
        Dictionary<int, object> _propertyIdDic = new Dictionary<int, object>();   
        Dictionary<object, object> _propertyDefValueDic = new Dictionary<object, object>();
        IPropertyDefinitions[] _definitions = null;        

        #endregion

        #region Public Properties

        public IEnumerable<object> Properties
        {
            get { return PropertyDefinitions.GetSupportedProperties(); }
        }

        public IPropertyDefinitions[] PropertyDefinitions
        {
            get
            {
                if (_definitions == null)
                {
                    _definitions = CofeServices.ServiceLocater.FindAllServices<IPropertyDefinitions>().ToArray();
                    refresh();
                }
                return _definitions;
            }
            set { _definitions = value; refresh(); }
        }

        public IPropertyInvoker PropertyInvoker { get; set; }
        public IPropertyAttributeHelper AttributeHelper { get; set; }
        
        #endregion







    }
}
